﻿using BaseMsg;
using ProtoBuf;
using System;
using System.IO;

namespace K.Client
{
    public class Msg
    {
        protected const int MAX_MSG_HEAD_LEN = 32000;
        protected const int MAX_MSG_BODY_LEN = 10 * 1024 * 1024;

        public delegate byte[] SerializeFunc(Msg msg);
        public delegate bool DeserializeFunc(Msg msg, MemoryStream ms);

        public static SerializeFunc SerializeHeadFunc;
        public static SerializeFunc SerializeBodyFunc;
        public static DeserializeFunc DeserializeHeadFunc;
        public static DeserializeFunc DeserializeBodyFunc;

        public int id;
        public MsgHead head;
        public IExtensible body;

        public static void Init(SerializeFunc vSerializeHeadFunc, SerializeFunc vSerializeBodyFunc,
                            DeserializeFunc vDeserializeHeadFunc, DeserializeFunc vDeserializeBodyFunc)
        {
            SerializeHeadFunc = vSerializeHeadFunc;
            SerializeBodyFunc = vSerializeBodyFunc;
            DeserializeHeadFunc = vDeserializeHeadFunc;
            DeserializeBodyFunc = vDeserializeBodyFunc;
        }

        public static Msg DeserializeMsg(byte[] ss, int startIdx, int len)
        {
            //消息格式
            //msg总长度|MessageHead长度|MessageHead|MessageBody长度|MessageBody|8字节对齐内容|
            Msg msg = new Msg();

            //1.解析出消息头长度
            var index = startIdx;
            int msgHeadLen = BitConverter.ToInt16(ss, index);
            if (msgHeadLen < 0 || msgHeadLen > MAX_MSG_HEAD_LEN)
            {
                Console.WriteLine("Msg head len < 0 or > {0}", MAX_MSG_HEAD_LEN);
                return null;
            }
            index += 2;

            //2.解析出消息头内容
            var bMsgHead = new byte[msgHeadLen];
            Array.Copy(ss, index, bMsgHead, 0, msgHeadLen);
            var msMsgHead = new MemoryStream(bMsgHead);
            if (!DeserializeHeadFunc(msg, msMsgHead))
            {
                return null;
            }
            index += msgHeadLen;

            //3.解析出消息体长度
            int msgBodyLen = BitConverter.ToInt32(ss, index);
            if (msgBodyLen < 0 || msgBodyLen > MAX_MSG_BODY_LEN)
            {
                Console.WriteLine("Msg body len < 0 or > {0}", MAX_MSG_BODY_LEN);
                return null;
            }
            index += 4;

            //4.解析出消息体内容
            var bMsgBody = new byte[msgBodyLen];
            Array.Copy(ss, index, bMsgBody, 0, msgBodyLen);
            var msMsgBody = new MemoryStream(bMsgBody);
            if (!DeserializeBodyFunc(msg, msMsgBody))
            {
                return null;
            }
            return msg;
        }


        public static byte[] SerializeMsg(Msg msg)
        {
            //消息格式
            //msg总长度|MessageHead长度|MessageHead|MessageBody长度|MessageBody|8字节对齐内容|

            //序列化 head
            byte[] msgHeadBytes = SerializeHeadFunc(msg);
            if (msgHeadBytes == null)
            {
                return null;
            }
            int msgHeadLen = msgHeadBytes.Length;
            if (msgHeadLen < 0 || msgHeadLen > MAX_MSG_HEAD_LEN)
            {
                Console.WriteLine("Msg head len < 0 or > {0}", MAX_MSG_HEAD_LEN);
                return null;
            }

            //序列化 body
            byte[] msgBodyBytes = SerializeBodyFunc(msg);
            if (msgBodyBytes == null)
            {
                return null;
            }
            int msgBodyLen = msgBodyBytes.Length;
            if (msgBodyLen < 0 || msgBodyLen > MAX_MSG_BODY_LEN)
            {
                Console.WriteLine("Msg body len < 0 or > {0}", MAX_MSG_BODY_LEN);
                return null;
            }
            //总长度
            int totolNum = 4 + 2 + msgHeadBytes.Length + 4 + msgBodyBytes.Length;

            //计算需要补齐的字节数
            int paddingNum = totolNum % 8;
            if (paddingNum != 0)
            {
                paddingNum = 8 - paddingNum;
            }
            //补齐数据
            var paddingBytes = new byte[paddingNum];
            totolNum += paddingNum;

            var ms = new MemoryStream();
            //写入总长度
            ms.Write(ConvertIntToByte(totolNum), 0, 4);
            //写入消息头长度
            ms.Write(ConvertIntToByte(msgHeadBytes.Length), 0, 2);
            //写入消息头
            ms.Write(msgHeadBytes, 0, msgHeadBytes.Length);
            //写入消息体长度
            ms.Write(ConvertIntToByte(msgBodyBytes.Length), 0, 4);
            //写入消息体
            ms.Write(msgBodyBytes, 0, msgBodyBytes.Length);
            if (paddingNum > 0)
            {
                //写入补齐数据
                ms.Write(paddingBytes, 0, paddingBytes.Length);
            }
            byte[] data = ms.ToArray();
            return data;
        }

        private static byte[] ConvertIntToByte(int d)
        {
            var re = BitConverter.GetBytes(d);
            //Array.Reverse(re); 
            return re;
        }

        private static byte[] ConvertLongToByte(long d)
        {
            var re = BitConverter.GetBytes(d);
            //Array.Reverse(re);
            return re;
        }
    }
}